﻿using System;
using System.Collections.Generic;
using System.Linq;
using ESRI.ArcGIS.Geometry;
using System.Runtime.InteropServices;

namespace ZJGIS.ArcGIS
{
    /// <summary>
    /// 概述：ESRI.ArcGIS.Geometry 转换类<br />
    /// </summary>
    [ComVisible(true)]
    public static class GeometryConvert
    {
        /// <summary>
        /// 将指定的 WKT 格式字符串的值转换为等效的 ESRI.ArcGIS.Client.IGeometry。
        /// </summary>
        /// <param name="wkt">需要进行转换的 WKT 格式的字符串。</param>
        /// <returns>等效于 WKT 格式字符串的值的 ESRI.ArcGIS.Client.IGeometry。</returns>
        /// <remarks>目前只支持多边形的转换。</remarks>
        public static IGeometry ToGeometry(string wkt)
        {
            if (string.IsNullOrEmpty(wkt))
            {
                return null;
            }

            if (wkt.ToUpper().Contains("POLYGON"))
            {
                return ToPolygon(wkt);
            }
            return null;
        }
        
        /// <summary>
        /// 将指定的 WKT 格式字符串的值转换为等效的 ESRI.ArcGIS.Geometry.IPolygon。
        /// </summary>
        /// <param name="wkt">需要进行转换的 WKT 格式的字符串。</param>
        /// <returns>等效于 WKT 格式字符串的值的 ESRI.ArcGIS.Geometry.IPolygon。</returns>
        public static IPolygon ToPolygon(string wkt)
        {
            if (string.IsNullOrEmpty(wkt))
            {
                return null;
            }

            try
            {
                if (wkt.ToUpper().Contains("MULTIPOLYGON"))
                {
                    IEnumerable<string> polygons = wkt.ToUpper().Replace("MULTIPOLYGON", "").Replace("(((", "").Replace(")))", "").Split(new string[] { ")),((" }, StringSplitOptions.RemoveEmptyEntries);
                    return PolygonsToMultiPolygon(polygons);
                }
                if (wkt.ToUpper().Contains("POLYGON"))
                {
                    IEnumerable<string> rings = wkt.ToUpper().Replace("POLYGON", "").Replace("((", "").Replace("))", "").Split(new string[] { "),(" }, StringSplitOptions.RemoveEmptyEntries);
                    return RingsToPolygon(rings);
                }
                return null;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="polygons"></param>
        /// <returns></returns>
        private static IPolygon PolygonsToMultiPolygon(IEnumerable<string> polygons)
        {
            IGeometryCollection geometryCollection = new Polygon() as IGeometryCollection;
            foreach (string polygon in polygons)
            {
                IEnumerable<string> rings = polygon.Split(new string[] { "),(" }, StringSplitOptions.RemoveEmptyEntries);
                IPolygon pg = RingsToPolygon(rings);
                geometryCollection.AddGeometry(pg as IGeometry);
            }
            return geometryCollection as IPolygon;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rings"></param>
        /// <returns></returns>
        private static IPolygon RingsToPolygon(IEnumerable<string> rings)
        {
            IGeometryCollection geometryCollection = new Polygon() as IGeometryCollection;
            foreach (string ring in rings)
            {
                IEnumerable<string> points = ring.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Reverse();   // [VEPT] OGC 规范中要求外环逆时针、内环顺时针，而 ArcGIS 要求外环顺时针、内环逆时针，于是顺序要反转
                IRing r = PointsToRing(points);
                geometryCollection.AddGeometry(r as IGeometry);
            }
            return geometryCollection as IPolygon;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        private static IRing PointsToRing(IEnumerable<string> points)
        {
            IPointCollection pointCollection = new Ring();
            foreach (string point in points)
            {
                string[] coordinates = point.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                IPoint p = CoordinatesToPoint(coordinates);
                pointCollection.AddPoint(p);
            }
            return pointCollection as IRing;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="coordinates"></param>
        /// <returns></returns>
        private static IPoint CoordinatesToPoint(string[] coordinates)
        {
            return new Point()
            {
                X = double.Parse(coordinates[0]),
                Y = double.Parse(coordinates[1])
            };
        }
    }
}